﻿/*
 *	Copyright (c) 2009 Queensland University of Technology. All rights reserved.
 *	The QUT Bioinformatics Collection is open source software released under the 
 *	Microsoft Public License (Ms-PL): http://www.microsoft.com/opensource/licenses.mspx.
 */
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace QUT.Bio.Util {
	/// <summary>
	/// Parses a list of name[value] name[value] pairs into an indexed data structure.
	/// </summary>
	
	public class Arguments : Dictionary<string, string> {
		
		/// <summary>
		/// Delegate type used to notify an invalid argument.
		/// </summary>
		/// <param name="args">The arguments object that contains the argument collection.</param>
		/// <param name="argumentName">The name of the invalid argument.</param>
		/// <returns></returns>
		
		public delegate string ArgumentNotFoundHandler( Arguments args, string argumentName );

		IntIndexer intIndexer;
		DoubleIndexer doubleIndexer;
		BoolIndexer boolIndexer;

		/// <summary>
		/// Called in the event that a request ed argument is not found.
		/// </summary>

		public ArgumentNotFoundHandler ArgumentNotFound {
			get;
			set;
		}

		/// <summary>
		/// Initialises the indexers useed to access integer, boolean and floating point arguments.
		/// </summary>
		
		public Arguments() {
			intIndexer = new IntIndexer {
				args = this
			};
			doubleIndexer = new DoubleIndexer {
				args = this
			};
			boolIndexer = new BoolIndexer {
				args = this
			};
		}

		/// <summary>
		/// Initialise this argument collection, parsing a set of command line arguments.
		/// </summary>
		/// <param name="args">The arugments to load.</param>
		
		public Arguments( string[] args )
			: this() {
			Load( args );
		}
		
		/// <summary>
		/// Initialises this argument collection, loading a dictionary of (name, value) pairs.
		/// </summary>
		/// <param name="args">The arguments to load.</param>
		
		public Arguments( IDictionary<string,string> args )
			: this () {
			foreach ( var x in args ) {
				Add( x.Key, x.Value );
			}
		}
		
		/// <summary>
		/// Parses a set of command line arguments and adds them to this collection.
		/// </summary>
		/// <param name="args">The arguments to load.</param>

		public void Load( string[] args ) {
			if ( args.Length == 0 ) {
				return;
			}
			
			string s = String.Join( " ", args );
			Regex regex = new Regex( "(?'argument'\\w+)(\\[(?'value'[^]]*)])?" );

			MatchCollection matches = regex.Matches( s );

			foreach ( Match x in matches ) {
				Group argument = x.Groups["argument"];
				Group value = x.Groups["value"];
				base[argument.Value] = value.Success ? value.Value : null;
			}
		}

		/// <summary>
		/// Get a string argument by name. If the requested argument is not found,
		/// the ArgumentNotFound delegate is called.
		/// </summary>
		/// <param name="argumentName">the name of the requested argument.</param>
		/// <returns>The named argument, if present, or null.</returns>
		
		public new string this[string argumentName] {
			get {
				if ( !ContainsKey( argumentName ) ) {
					if ( ArgumentNotFound != null ) {
						return ArgumentNotFound( this, argumentName );
					}
					else {
						return null;
					}
				}
				else {
					return base[argumentName];
				}
			}
			set {
				base[argumentName] = value;
			}
		}

		/// <summary>
		/// Gets an indexer that can lookup integer arguments by name.
		/// </summary>
		
		public Indexer<string, int> Int {
			get {
				return intIndexer;
			}
		}
		
		/// <summary>
		/// gets an indexer that can lookup floating point arguments by name.
		/// </summary>
	
		public Indexer<string, Double> Double {
			get {
				return doubleIndexer;
			}
		}
		
		/// <summary>
		/// Gets an indexer that can lookup boolean arguments by name.
		/// </summary>
		
		public Indexer<string, bool> Bool {
			get {
				return boolIndexer;
			}
		}

		/// <summary>
		/// Implement the integer-lookup indexer.
		/// </summary>
		
		public class IntIndexer : Indexer<string, int> {
			internal Arguments args;

			/// <summary>
			/// Gets an integer argument by name.
			/// </summary>
			/// <param name="i">The name of the requested argument.</param>
			/// <returns>The requested value.</returns>
			
			public int this[string i] {
				get {
					try {
						return int.Parse( args[i] );
					}
					catch {
						return int.MinValue;
					}
				}
				set {
					args[i] = value.ToString();
				}
			}

			/// <summary>
			/// Not implemented.
			/// </summary>
			/// <returns></returns>
			
			public IEnumerator<int> GetEnumerator () {
				throw new NotImplementedException();
			}

			/// <summary>
			/// Not implemented.
			/// </summary>
			/// <returns></returns>
			
			IEnumerator IEnumerable.GetEnumerator () {
				throw new NotImplementedException();
			}
		}

		/// <summary>
		/// Implement the float-lookup indexer.
		/// </summary>

		public class DoubleIndexer : Indexer<string, double> {
			internal Arguments args;

			/// <summary>
			/// Gets an floating point argument by name.
			/// </summary>
			/// <param name="i">The name of the requested argument.</param>
			/// <returns>The requested value.</returns>

			public double this[string i] {
				get {
					try {
						return double.Parse( args[i] );
					}
					catch {
						return double.NaN;
					}
				}
				set {
					args[i] = value.ToString();
				}
			}

			/// <summary>
			/// Not implemented.
			/// </summary>
			/// <returns></returns>

			public IEnumerator<double> GetEnumerator () {
				throw new NotImplementedException();
			}

			/// <summary>
			/// Not implemented.
			/// </summary>
			/// <returns></returns>

			IEnumerator IEnumerable.GetEnumerator () {
				throw new NotImplementedException();
			}
		}

		/// <summary>
		/// Implement the boolean lookup indexer.
		/// </summary>

		public class BoolIndexer : Indexer<string, bool> {
			internal Arguments args;

			/// <summary>
			/// Gets an floating point argument by name.
			/// </summary>
			/// <param name="i">The name of the requested argument.</param>
			/// <returns>The requested value.</returns>

			public bool this[string i] {
				get {
					if ( args.ContainsKey( i ) ) {
						return args[i].ToLower() == "true";
					}
					else {
						return false;
					}
				}
				set {
					args[i] = value.ToString();
				}
			}

			/// <summary>
			/// Not implemented.
			/// </summary>
			/// <returns></returns>

			public IEnumerator<bool> GetEnumerator () {
				throw new NotImplementedException();
			}

			/// <summary>
			/// Not implemented.
			/// </summary>
			/// <returns></returns>

			IEnumerator IEnumerable.GetEnumerator () {
				throw new NotImplementedException();
			}
		}
	}
}
